package server;

import java.rmi.RemoteException;
import java.sql.Date;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import common.Game;
import common.IUI;
import common.Player;
import common.Profile;
import common.ReverseIntegerComparator;

public class profiledGame implements Runnable {
	
	List<Profile> profileList = new ArrayList<Profile>();
	List<IUI> Uis;
	DBLogic DBL;
	
	public profiledGame(List<Profile> profileList, List<IUI> Uis, DBLogic DBL) {
		this.profileList = profileList;
		this.Uis = Uis;
		this.DBL = DBL;
	}
	
    public void run() {
		int i = 0, j = 0;
		String name;
		List<String> namesList = new ArrayList<String>();
		List<Profile> toRemove = new ArrayList<Profile>();
		Integer[] listOfMoney = new Integer[6];
		
		for (Profile profile : profileList) {
			namesList.add(profile.getUserName());
		}
		Game NewGame = new Game(namesList,Uis,false);
		NewGame.startGame();
		
		Map<Integer,List<Player>> finalStandings = NewGame.getFinalStandings();
		finalStandings.keySet().toArray(listOfMoney);
		
		for(i = 0; i<finalStandings.size(); i++){
			for(j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++){
				name = finalStandings.get(listOfMoney[i]).get(j).getName();

				for(Profile p : profileList){
					if (p.getUserName().equals(name) && p.isGuest()){
						toRemove.add(p);
						finalStandings.get(listOfMoney[i]).remove(j);
					}
				}
			}
		}
		for(Profile p : toRemove){
			profileList.remove(p);
		}
		
		if (profileList.size() > 1) {
    		List<Integer> ratingsBeforeUpdate = new ArrayList<Integer>();
    		for (Profile p : profileList) {
    		    ratingsBeforeUpdate.add(p.getRating());
    		}
    		updateRanking(profileList, finalStandings);
    		List<Integer> ratingsAfterUpdate = new ArrayList<Integer>();
    		for (Profile p : profileList) {
    		    ratingsAfterUpdate.add(p.getRating());
    		}
    		
    		Date gameDate = new Date(System.currentTimeMillis());
    		for (Profile profile : profileList) {
    		    List<Profile> rivalProfiles = new ArrayList<Profile>(profileList);
    		    rivalProfiles.remove(profile);
    		    List<String> rivalNames = new ArrayList<String>();
    		    for (Profile p : rivalProfiles) {
    		        rivalNames.add(p.getUserName());
    		    }
    		    
    		    int delta = ratingsAfterUpdate.get(0) - ratingsBeforeUpdate.get(0);		
    		    ratingsAfterUpdate.remove(0);
    		    ratingsBeforeUpdate.remove(0);
    
    		    profile.addHistoryEntry(gameDate, rivalNames, delta);
    		    DBL.updateProfile(profile);
    		}

    		Map<Integer, List<String>> newHallOfFame;
    		try {
    		    newHallOfFame = updateHallOfFame(DBL.getHallOfFame(), finalStandings);
    		    DBL.updateHallOfFame(newHallOfFame);
    		} catch (SQLException e2) {
    		    e2.printStackTrace();
    		}
    		
    		for(IUI ui : Uis){
    		    try {
    		        ui.printMessage("\n\n");
    		    } catch (RemoteException e1) {
    		        e1.printStackTrace();
    		    }
    		    for(Profile p : profileList){
    		        try {
    		            ui.printMessage(p.getUserName() +"'s new rating is "+ p.getRating());
    		        } catch (RemoteException e) {
    		            e.printStackTrace();
    		        }    
    		    }
    		}
		}
		// Close connection to connected users.
		for (IUI ui : Uis) {
            try {
                ui.closeExportedObject();
            } catch (RemoteException e) {
                System.err.println("Error while calling remote UI");
            }
		}
	}
	
	public void updateRanking(List<Profile> profileList,Map<Integer,List<Player>> finalStandings) {
		
		int j=0,i=0;
		Integer[] listOfMoney = new Integer[6];
		finalStandings.keySet().toArray(listOfMoney);
		int LosersRating = 0;
		int WinnersRating = 0;
		
		if (profileList.size()>1){
		Profile pro = null;
		int counter = 0;
		int[] points = new int[profileList.size()];
		
		if (profileList.size()%2 == 0){
		for(i = 0 ; i <  profileList.size()/2; i++){
			points[i] = ((profileList.size()/2)-i)*20;
		}
		for(i = profileList.size()-1 ; i >=  profileList.size()/2; i--){
			points[i] = ((profileList.size()/2)-1-i)*20;
		}
		}
		else {
			for(i = 0 ; i <  profileList.size(); i++){
				points[i] = ((profileList.size()/2)-i)*20;
			}
		}
		
		int[] actualPoints = new int[finalStandings.size()];
		for(i = 0 ; i < finalStandings.size(); i++){
				for(j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++){
					actualPoints[i] = actualPoints[i] + points[counter];
					counter++;
			}
		}	
	
		int numOfPlayers = profileList.size();
		Set<Profile> winners = new HashSet<Profile>(); 
		Set<Profile> losers = new HashSet<Profile>(); 
		int[] updatedRatings = new int[numOfPlayers];
		int k = 0;
		int m = 0;
		boolean bool = false;
		
		for(Profile p : profileList){
			losers.add(p);
		}
		
		for (i = 0; i < actualPoints.length; i++){
			for (j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++){

					Player curPlayer = finalStandings.get(listOfMoney[i]).get(j);
					String curPlayerName = curPlayer.getName();

					for(Profile p : profileList)
					{
						if(curPlayerName.equals(p.getUserName()))
						{
							pro = p;
							if(winners.contains(pro)) winners.remove(pro);
							if(losers.contains(pro)) losers.remove(pro);
						}
						else
						{
							for (m = 0; m < finalStandings.get(listOfMoney[i]).size(); m++){
								if (finalStandings.get(listOfMoney[i]).get(m).getName().equals(p.getUserName())){
									bool = true;
									break;
								}
							}
							if(!winners.contains(p) && bool){
								losers.remove(p);
								bool = false;
							}	
						}
					}
					int losersum = 0;
					int winnersum = 0;
					for(Profile p : losers)
					{
						losersum += p.getRating();	
					}
					
					for(Profile p : winners)
					{
						winnersum += p.getRating();	
					}
					if (losers.size() != 0){
				    LosersRating = losersum/losers.size();
					}
					if (winners.size() != 0){
					WinnersRating = winnersum/winners.size();
					}
					int NewRating = 0;
					NewRating = Math.max(0, pro.getRating() + actualPoints[i]/finalStandings.get(listOfMoney[i]).size() - Math.min(((pro.getRating()-LosersRating)/20),5*losers.size()) + Math.max(((WinnersRating-pro.getRating())/20),(-5)*winners.size()));
					updatedRatings[k] = NewRating;
					k++;
					
					
				}
			int t = 0; 
			for(Profile prof : profileList){
				for (t = 0; t < finalStandings.get(listOfMoney[i]).size(); t++){
					if (finalStandings.get(listOfMoney[i]).get(t).getName().equals(prof.getUserName())){
						winners.add(prof);
						break;
					}
				}
				}
			}
		k = 0;
			for (i = 0; i < actualPoints.length; i++) {
				for (j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++) {
				
					Player curPlayer = finalStandings.get(listOfMoney[i]).get(j);
					String curPlayerName = curPlayer.getName();
			
					for(Profile p : profileList) {
						if (curPlayerName.equals(p.getUserName()))
							p.setRating(updatedRatings[k]);	
					}
					k++;
				}
			}
		}
	}

    public Map<Integer, List<String>> updateHallOfFame(
            Map<Integer, List<String>> hallOfFame,
            Map<Integer, List<Player>> finalStandings) {

        final int SIZE_OF_TABLE = 10;
        ReverseIntegerComparator comparator = new ReverseIntegerComparator();
        int minimumForHallOfFame = 0;
        int index = 0;

        index = 1;
        for (int cash : hallOfFame.keySet()) {
            index += hallOfFame.get(cash).size();
            if (index > SIZE_OF_TABLE) {
                minimumForHallOfFame = cash;
                break;
            }
        }

        for (int currentCash : finalStandings.keySet()) {
            if (minimumForHallOfFame < currentCash) {
                List<String> sameCashNames = new ArrayList<String>();
                for (Player player : finalStandings.get(currentCash)) {
                    sameCashNames.add(player.getName());
                }

                if (hallOfFame.containsKey(currentCash)) {
                    hallOfFame.get(currentCash).addAll(sameCashNames);
                } else {
                    hallOfFame.put(currentCash, sameCashNames);
                }
            }
        }

        index = 0;
        Map<Integer, List<String>> newHOF = new TreeMap<Integer, List<String>>(
                comparator);
        for (int cash : hallOfFame.keySet()) {
            if (index + hallOfFame.get(cash).size() <= SIZE_OF_TABLE) {
                index += hallOfFame.get(cash).size();
                newHOF.put(cash, hallOfFame.get(cash));
            } else {
                List<String> currentCashPlayers = new ArrayList<String>();
                for (int i = 0; index < SIZE_OF_TABLE
                        && i < hallOfFame.get(cash).size(); i++, index++) {
                    currentCashPlayers.add(hallOfFame.get(cash).get(i));
                }
                newHOF.put(cash, currentCashPlayers);
                break;
            }
        }
        return newHOF;
    }
}
	
	

